#ifndef __NETWORKLYRICFILE_H__
#define __NETWORKLYRICFILE_H__

#include "Common/NetworkSocket.h"
#include "Common/NetworkProcessor.h"
#include "CachedFileManager.h"

namespace SPlayer
{
    namespace Network
    {
        using namespace System;
        using namespace System::Net;
        using namespace System::Net::Sockets;
        using namespace System::Collections;
        using namespace System::Collections::Generic;
        using namespace SPlayer;
        using namespace System::IO;


        enum class NetworkLyricFileState
        {
            Ready,
            Sent,
            Received,
            Error
        };
        ref class NetworkLyricFileEventDispatcher
        {
        public:
            Action ^onDownloadFinished;
            Action ^onError;
        };
        ref class NetworkLyricFile
        {
        public:
            NetworkLyricFile(String ^musicTitle)
                : musicTitle(musicTitle)
            {
                if (CachedFileManager::isLyricFileCached(MusicPathInfo::toNetworkPath(musicTitle)))
                {
                    FileStream f(CachedFileManager::toLyricCachedPath(MusicPathInfo::toNetworkPath(musicTitle)), FileMode::Open, FileAccess::Read, FileShare::ReadWrite);

                    this->fileSize = static_cast<int>(f.Length);
                    this->filedata = gcnew array<unsigned char>(fileSize);
                    f.Read(filedata, 0, fileSize);
                    state = NetworkLyricFileState::Received;
                    f.Close();
                }
            }
            void startDownload()
            {
                if (state == NetworkLyricFileState::Sent || state == NetworkLyricFileState::Received)
                {
                    // downloading or downloaded
                    return;
                }
                state = NetworkLyricFileState::Ready;

                socket = gcnew NetworkSocket;
                socket->connectAsync();
                socket->eventDispatcher->onConnectionFinished =
                    gcnew Action(this, &NetworkLyricFile::onConnected);
            }
            void abort()
            {
                if (state == NetworkLyricFileState::Sent)
                {
                    state = NetworkLyricFileState::Error;
                    // abort, don't call error
                    //Function::InvokeSync(eventDispatcher->onError);
                }
                if (socket)
                {
                    socket->Close();
                }
                if (processor)
                {
                    processor->close();
                }
            }
        protected:
            void onConnected()
            {
                if (socket->Connected)
                {
                    processor = gcnew NetworkProcessor(socket);
                    processor->eventDispatcher->onParamReceived +=
                        gcnew Action(this, &NetworkLyricFile::onParamReceived);
                    processor->eventDispatcher->onDataReceiveFinished +=
                        gcnew Action(this, &NetworkLyricFile::onDataAllReceived);
                    processor->eventDispatcher->onError +=
                        gcnew Action<Exception ^>(this, &NetworkLyricFile::onError);
                    processor->eventDispatcher->onClosed +=
                        gcnew Action(this, &NetworkLyricFile::onClosed);

                    auto params = processor->sendParams;
                    params->add("Request", "DownloadLyric");
                    params->add("Title", musicTitle);
                    processor->sendAsync();
                    state = NetworkLyricFileState::Sent;
                }
                else if (state != NetworkLyricFileState::Error)
                {
                    state = NetworkLyricFileState::Error;
                    Function::InvokeSync(eventDispatcher->onError);
                }
            }
            void onParamReceived()
            {
                fileSize = processor->receivedParams["FileSize"];
                filedata = gcnew array<unsigned char>(fileSize);
            }
            void onDataAllReceived()
            {
                processor->receivedData->CopyTo(filedata, 0);

                processor->close();

                state = NetworkLyricFileState::Received;
                Common::Function::InvokeSync(eventDispatcher->onDownloadFinished);
            }
            void onClosed()
            {
                if (state == NetworkLyricFileState::Sent)
                {
                    state = NetworkLyricFileState::Error;
                    Function::InvokeSync(eventDispatcher->onError);
                }
                socket = nullptr;
                processor = nullptr;
            }
            void onError(System::Exception ^e)
            {
                processor->close();
            }
        public:
            String ^musicTitle;
            array<unsigned char> ^filedata;
            int fileSize = 0;
            NetworkLyricFileState state = NetworkLyricFileState::Ready;

            NetworkSocket ^socket = nullptr;
            NetworkProcessor ^processor = nullptr;

            NetworkLyricFileEventDispatcher ^eventDispatcher = gcnew NetworkLyricFileEventDispatcher;
        };
    }
}
#endif // __NETWORKLYRICFILE_H__